//
//  LBViewCaptureModule.m
//  LBReact
//
//  Created by WuShiHai on 19/5/24.
//  Copyright (c) 2019 Lebbay. All rights reserved.
//

#import "LBViewCaptureModule.h"
#import <AVFoundation/AVFoundation.h>
#import <React/RCTLog.h>
#import <React/UIView+React.h>
#import <React/RCTUtils.h>
#import <React/RCTConvert.h>
#import <React/RCTScrollView.h>
#import <React/RCTUIManager.h>
#import <React/RCTUIManagerUtils.h>
#import <React/RCTBridge.h>
#import <Photos/Photos.h>
#import <LBFoundation/LBFoundation.h>
#import <LBUIKit/LBUIKit.h>
#import "LBRConstants.h"

@implementation LBViewCaptureModule

RCT_EXPORT_MODULE(viewCapture)

@synthesize bridge = _bridge;

- (dispatch_queue_t)methodQueue
{
    return RCTGetUIManagerQueue();
}

+ (BOOL)requiresMainQueueSetup {
    return YES;
}

- (NSDictionary *)constantsToExport
{
    return @{
             @"CacheDir" : [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject],
             @"DocumentDir": [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject],
             @"MainBundleDir" : [[NSBundle mainBundle] bundlePath],
             @"MovieDir": [NSSearchPathForDirectoriesInDomains(NSMoviesDirectory, NSUserDomainMask, YES) firstObject],
             @"MusicDir": [NSSearchPathForDirectoriesInDomains(NSMusicDirectory, NSUserDomainMask, YES) firstObject],
             @"PictureDir": [NSSearchPathForDirectoriesInDomains(NSPicturesDirectory, NSUserDomainMask, YES) firstObject],
             };
}

// forked from RN implementation
// https://github.com/facebook/react-native/blob/f35b372883a76b5666b016131d59268b42f3c40d/React/Modules/RCTUIManager.m#L1367

RCT_EXPORT_METHOD(takeSnapshot:(nonnull NSNumber *)target
                  withOptions:(NSDictionary *)options
                  resolve:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject)
{
    [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
        
        // Get view
        UIView *view;
        view = viewRegistry[target];
        if (!view) {
            reject(RCTErrorUnspecified, [NSString stringWithFormat:@"No view found with reactTag: %@", target], nil);
            return;
        }
        
        // Get options
        CGSize size = [RCTConvert CGSize:options];
        NSString *format = [RCTConvert NSString:options[@"format"] ?: @"png"];
        NSString *result = [RCTConvert NSString:options[@"result"] ?: @"file"];
        BOOL snapshotContentContainer = [RCTConvert BOOL:options[@"snapshotContentContainer"] ?: @"false"];
        
        // Capture image
        BOOL success;
        
        UIView* rendered;
        UIScrollView* scrollView;
        if (snapshotContentContainer) {
            if (![view isKindOfClass:[RCTScrollView class]]) {
                reject(RCTErrorUnspecified, [NSString stringWithFormat:@"snapshotContentContainer can only be used on a RCTScrollView. instead got: %@", view], nil);
                return;
            }
            RCTScrollView* rctScrollView = (RCTScrollView *)view;
            scrollView = rctScrollView.scrollView;
            rendered = scrollView;
        }
        else {
            rendered = view;
        }
        
        if (size.width < 0.1 || size.height < 0.1) {
            size = snapshotContentContainer ? scrollView.contentSize : view.bounds.size;
        }
        if (size.width < 0.1 || size.height < 0.1) {
            reject(RCTErrorUnspecified, [NSString stringWithFormat:@"The content size must not be zero or negative. Got: (%g, %g)", size.width, size.height], nil);
            return;
        }
        
        CGPoint savedContentOffset = CGPointZero;
        CGRect savedFrame = CGRectZero;
        if (snapshotContentContainer) {
            // Save scroll & frame and set it temporarily to the full content size
            savedContentOffset = scrollView.contentOffset;
            savedFrame = scrollView.frame;
            scrollView.contentOffset = CGPointZero;
            scrollView.frame = CGRectMake(0, 0, scrollView.contentSize.width, scrollView.contentSize.height);
        }
        UIGraphicsBeginImageContextWithOptions(size, NO, 0);
        success = [rendered drawViewHierarchyInRect:(CGRect){CGPointZero, size} afterScreenUpdates:YES];
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        
        if (snapshotContentContainer) {
            // Restore scroll & frame
            scrollView.contentOffset = savedContentOffset;
            scrollView.frame = savedFrame;
        }
        
        if (!success) {
            reject(RCTErrorUnspecified, @"The view cannot be captured. drawViewHierarchyInRect was not successful. This is a potential technical or security limitation.", nil);
            return;
        }
        
        if (!image) {
            reject(RCTErrorUnspecified, @"Failed to capture view snapshot. UIGraphicsGetImageFromCurrentImageContext() returned nil!", nil);
            return;
        }
        
        // Convert image to data (on a background thread)
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            
            NSData *data;
            if ([format isEqualToString:@"png"]) {
                data = UIImagePNGRepresentation(image);
            } else if ([format isEqualToString:@"jpeg"] || [format isEqualToString:@"jpg"]) {
                CGFloat quality = [RCTConvert CGFloat:options[@"quality"] ?: @1];
                data = UIImageJPEGRepresentation(image, quality);
            } else {
                reject(RCTErrorUnspecified, [NSString stringWithFormat:@"Unsupported image format: %@. Try one of: png | jpg | jpeg", format], nil);
                return;
            }
            
            NSError *error = nil;
            NSString *res = nil;
            if ([result isEqualToString:@"file"]) {
                // Save to a temp file
                NSString *path;
                if (options[@"path"]) {
                    path = options[@"path"];
                    NSString * folder = [path stringByDeletingLastPathComponent];
                    NSFileManager * fm = [NSFileManager defaultManager];
                    if(![fm fileExistsAtPath:folder]) {
                        [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:&error];
                        [fm createFileAtPath:path contents:nil attributes:nil];
                    }
                }
                else {
                    path = RCTTempFilePath(format, &error);
                }
                if (path && !error) {
                    if ([data writeToFile:path options:(NSDataWritingOptions)0 error:&error]) {
                        res = path;
                    }
                }
            }
            else if ([result isEqualToString:@"base64"]) {
                // Return as a base64 raw string
                res = [data base64EncodedStringWithOptions: NSDataBase64Encoding64CharacterLineLength];
            }
            else if ([result isEqualToString:@"data-uri"]) {
                // Return as a base64 data uri string
                NSString *base64 = [data base64EncodedStringWithOptions: NSDataBase64Encoding64CharacterLineLength];
                res = [NSString stringWithFormat:@"data:image/%@;base64,%@", format, base64];
            }
            else {
                reject(RCTErrorUnspecified, [NSString stringWithFormat:@"Unsupported result: %@. Try one of: file | base64 | data-uri", result], nil);
                return;
            }
            if (res && !error) {
                resolve(res);
                return;
            }
            
            // If we reached here, something went wrong
            if (error) reject(RCTErrorUnspecified, error.localizedDescription, error);
            else reject(RCTErrorUnspecified, @"viewshot unknown error", nil);
        });
    }];
}

RCT_EXPORT_METHOD(saveImage:(NSString *)filePath
                  content:(NSString *)content
                  callback:(RCTResponseSenderBlock)callback) {
    beginExecInMainBlock
    UIImage *image = nil;
    if (filePath && [filePath isKindOfClass:[NSString class]]) {
        image = [UIImage imageWithContentsOfFile:filePath];
    }
    if (!image && content) {
        NSData *data = [[NSData alloc] initWithBase64EncodedString:content
                                                           options:NSDataBase64DecodingIgnoreUnknownCharacters];
        image = [UIImage imageWithData:data];
    }
    if (!image) {
        LBBlockSafeRun(callback, @[@(false)]);
        return;
    }
    void(^saveBlock)() = ^() {
        dispatch_async(dispatch_get_main_queue(), ^{
            NSError *error =nil;
            [[PHPhotoLibrary sharedPhotoLibrary]performChangesAndWait:^{
                [PHAssetChangeRequest creationRequestForAssetFromImage:image];
            } error:&error];
            LBBlockSafeRun(callback, @[@(error == nil)]);
        });
    };
    PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
    if (status == PHAuthorizationStatusNotDetermined) {
        [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
            dispatch_async(dispatch_get_main_queue(), ^{
                if (status == PHAuthorizationStatusAuthorized) {
                    LBBlockSafeRun(saveBlock);
                }else {
                    LBBlockSafeRun(callback, @[@(false)]);
                }
            });
        }];
    } else if (status == PHAuthorizationStatusAuthorized) {
        LBBlockSafeRun(saveBlock);
    } else {
        [self alertControllerWithTitle:@"此应用没有权限访问您的照片或视频"
                               message:@"您可以在“隐私设置”中启动访问"
                           cancelTitle:@"取消"
                          defalutTitle:@"设置"
                        defaultHandler:^{
                            [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
                        }];
        LBBlockSafeRun(callback, @[@(false)]);
    }
    endExecInMainBlock
}

- (void)alertControllerWithTitle:(NSString *)title
                         message:(NSString *)message
                     cancelTitle:(NSString *)cancelTitle
                    defalutTitle:(NSString *)defaultTitle
                  defaultHandler:(void(^)(void))defaultHandler {
//    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
//    UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:cancelTitle style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
//    }];
//    [alertController addAction:cancelAction];
//
//    UIAlertAction *settingAction = [UIAlertAction actionWithTitle:defaultTitle style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
//        if (defaultHandler) {
//            defaultHandler();
//        }
//    }];
//    [alertController addAction:settingAction];
//    [[UIApplication displayViewController] presentViewController:alertController
//                                                        animated:YES
//                                                      completion:nil];
}

@end
